﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;

namespace Qreed.Drawing.Drawing2D
{
    public class Polygon2DOld
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Polygon2D"/> class.
        /// </summary>
        /// <param name="vertices">The vertices.</param>
        public Polygon2DOld(IEnumerable<PointF> vertices)
        {
            _vertices = new List<PointF>(vertices);
            Edges.Add(new LineSegment2DOld(this, _vertices.ToArray()));

            Validate();
        }

        private List<PointF> _vertices;
        /// <summary>
        /// Gets the vertices.
        /// </summary>
        /// <value>The vertices.</value>
        public List<PointF> Vertices
        {
            get { return this._vertices; }
            internal set { this._vertices = value; }
        }

        public List<LineSegment2DOld> _edges = new List<LineSegment2DOld>();
        /// <summary>
        /// Gets the edges.
        /// </summary>
        /// <value>The edges.</value>
        public List<LineSegment2DOld> Edges
        {
            get { return _edges; }
        }

        /// <summary>
        /// Gets the <see cref="System.Drawing.PointF"/> at the specified index.
        /// </summary>
        /// <value></value>
        public PointF this[int index]
        {
            get
            {
                index = index % Vertices.Count;
                index = index < 0 ? index + Vertices.Count : index;

                return Vertices[index];
            }
        }

        /// <summary>
        /// Validates the vertices.
        /// </summary>
        public virtual void Validate()
        {
            if (Vertices.Count < 3)
                throw (new ApplicationException("Need at least 3 vertices to form a polygon."));

            for (int i = 0; i < Vertices.Count; i++)
            {
                Line2D edge = new Line2D(this[i], this[i + 1]);

                PointF? point = GetIntersectionPoint(edge);

                if (point.HasValue)
                    throw (new ApplicationException("Only simple polygons are supported."));
            }
        }

        /// <summary>
        /// Locates the intersection point.
        /// </summary>
        /// <param name="line">The line.</param>
        /// <returns>The point of intersection or <c>null</c> if the line doesn't intersects.</returns>
        public PointF? GetIntersectionPoint(Line2D line)
        {
            for (int i = 0; i < Vertices.Count; i++)
            {
                Line2D edge = new Line2D(this[i], this[i + 1]);

                PointF? point = line.GetIntersectionPoint(edge, false);

                if (point.HasValue)
                    return point;
            }

            return null;
        }

        /// <summary>
        /// Locates the intersection points between this and another polygon.
        /// </summary>
        /// <param name="other">The other polygone.</param>
        /// <param name="isStopOnFirstHit">if set to <c>true</c> the function will return only the first intersection point found.</param>
        /// <returns>An array of intersection points.</returns>
        public PointF[] GetIntersectionPoints(Polygon2DOld other, bool isStopOnFirstHit)
        {
            List<PointF> intersectionPoints = new List<PointF>();

            for (int i = 0; i < Vertices.Count; i++)
            {
                Line2D edge = new Line2D(this[i], this[i + 1]);

                PointF? point = other.GetIntersectionPoint(edge);

                if (point.HasValue)
                {
                    intersectionPoints.Add(point.Value);

                    if (isStopOnFirstHit)
                        break;
                }
            }

            return intersectionPoints.ToArray();
        }

        /// <summary>
        /// Determines whether the polygon contains the specified pt.
        /// </summary>
        /// <param name="pt">The point to check.</param>
        /// <returns>
        /// 	<c>true</c> if the polygon contains the specified point; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        /// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
        /// </remarks>
        public bool Contains(PointF pt)
        {
            bool c = false;

            int i, j;

            for (i = 0, j = this.Vertices.Count - 1; i < this.Vertices.Count; j = i++)
            {
                float vertx_i = this.Vertices[i].X;
                float verty_i = this.Vertices[i].Y;
                float vertx_j = this.Vertices[j].X;
                float verty_j = this.Vertices[j].Y;

                if (((verty_i > pt.Y) != (verty_j > pt.Y)) &&
                    (pt.X < (vertx_j - vertx_i) * (pt.Y - verty_i) / (verty_j - verty_i) + vertx_i))
                {
                    c = !c;
                }
            }

            return c;
        }

        /// <summary>
        /// Determines whether the polygon contains the specified polygon.
        /// </summary>
        /// <param name="polygon">The polygon.</param>
        /// <returns>
        /// 	<c>true</c> if the polygon contains the specified polygon; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(Polygon2DOld polygon)
        {
            foreach (PointF pt in polygon.Vertices)
            {
                if (!Contains(pt))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Gets the index of the closest vertex on the edge to the given point.
        /// </summary>
        /// <param name="pt">The point to check.</param>
        /// <param name="length">The length between the edge and the given point.</param>
        /// <returns>The index of the vertex.</returns>
        public int GetClosestEdgeIndex(PointF pt, ref float length)
        {
            int bestIndex = 0;
            float bestLength = float.MaxValue;

            for (int i = 0; i < Vertices.Count; i++)
            {
                PointF edgePoint = Vertices[i];

                float currentLength = GetVectorLength(pt, edgePoint);

                if (currentLength <= bestLength)
                {
                    bestIndex = i;
                    bestLength = currentLength;
                }
            }

            length = bestLength;

            return bestIndex;
        }

        private int[] GetClosestEdgeIndex(Polygon2DOld polygon, ref float length, Polygon2DOld[] holes)
        {
            int[] bestIndex = { 0, 0 };
            float bestLength = float.MaxValue;

            for (int i = 0; i < polygon.Vertices.Count; i++)
            {
                PointF pt = polygon.Vertices[i];
                float currentLength = 0.0f;

                int currentIndex = GetClosestEdgeIndex(pt, ref currentLength);

                if (currentLength <= bestLength)
                {
                    bestIndex[0] = currentIndex;
                    bestIndex[1] = i;
                    bestLength = currentLength;
                }
            }

            length = bestLength;

            return bestIndex;
        }

        /// <summary>
        /// Gets the index of the closest edge.
        /// </summary>
        /// <param name="polygon">The polygon.</param>
        /// <param name="length">The length.</param>
        /// <returns></returns>
        public int[] GetClosestEdgeIndex(Polygon2DOld polygon, ref float length)
        {
            return GetClosestEdgeIndex(polygon, ref length, null);
        }

        /// <summary>
        /// Gets the length of the vector.
        /// </summary>
        /// <param name="ptA">The pt A.</param>
        /// <param name="ptB">The pt B.</param>
        /// <returns></returns>
        public static float GetVectorLength(PointF ptA, PointF ptB)
        {
            PointF v = new PointF(ptB.X - ptA.X, ptB.Y - ptA.Y);
            float length = (float)Math.Sqrt((double)(v.X * v.X + v.Y * v.Y));
            return length;
        }

        /// <summary>
        /// Subtracts the given polygons from this instance.
        /// </summary>
        /// <param name="polygons">The holes to drill.</param>
        /// <remarks>Currently only non-intersecting polygons are supported.</remarks>
        public void Subtract(Polygon2DOld[] polygons)
        {
            for (int i = 0; i < polygons.Length; i++)
            {
                Polygon2DOld hole = polygons[i];

                if (!Contains(hole))
                    continue;
                
                Edges.Add(new LineSegment2DOld(this, hole.Vertices.ToArray()));

                /*PointF[] intersections = GetIntersectionPoints(hole, true);

                if (intersections.Length != 0)
                    throw (new ApplicationException("Cannot drill hole with an intersecting polygon."));*/

                // Locate the vertex that is closest to to one of the vertices of the hole

                float length = 0.0f;
                int[] indices = GetClosestEdgeIndex(hole, ref length);

                // Merge the list of vertices by adding a line that connects both polygons

                Vertices.Add(new PointF()); // Add a dummy vertice to the end of the list which we remove later on

                // Duplicate the points that will start the connection so they close it too

                int insertAt = indices[0] + 1;

                Vertices.Insert(insertAt++, hole.Vertices[indices[1]]);
                Vertices.Insert(insertAt, Vertices[indices[0]]);

                for (int j = 0; j < hole.Vertices.Count; j++)
                {
                    int getAt = indices[1] + j;
                    PointF pt = hole[getAt];

                    Vertices.Insert(insertAt, pt); // Note: this will change the winding order of the hole polygon
                }

                Vertices.RemoveAt(Vertices.Count - 1); // Remove the dummy vertice
            }

            Validate();
        }

        public Polygon2DOld[] Triangulate()
        {
            List<Polygon2DOld> triangleList = new List<Polygon2DOld>();
            


            return triangleList.ToArray();
        }
    }

    
}
